package casper.alpha.utils;

import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;

public class CasperDataGenerator {
	private static final Pattern pattern = Pattern.compile("\\(([0-9]*\\.?[0-9]+),([0-9]*\\.?[0-9]+)\\)");
	
	public Coordinate randomCoordinateInGeometry(Geometry bound) {
		Random rand = new Random(System.currentTimeMillis());
		return new Coordinate(rand.nextDouble() * 180 - 90 , rand.nextDouble() * 360 - 180);
	}
	
	static final double maxy = 106.834955;
	static final double miny = 106.694745;
	static final double maxx = 10.84973;
	static final double minx = 10.769125;
	public void distributeUniformPublicData(int n) {
		// 10.000 = 100 * 100;
		new CasperQuery().clearPublicData();
		double area = (maxx - minx) * (maxy - miny);
		System.out.println("area = " + area + " nobjects = " + n);
		double sqrtn = Math.sqrt(n);
		double gridx = (maxx - minx) / sqrtn;
		double gridy = (maxy - miny) / sqrtn;
		int isqrtn = (int)sqrtn;
		for (int i = 0; i < isqrtn; i++) {
			for (int j = 0; j < isqrtn; j++) {
				double offsetx = minx + i * gridx;
				double offsety = miny + j * gridy;
				Coordinate c = new Coordinate(offsetx, offsety);
				new CasperQuery().insertPublicData(c, i * isqrtn + j);
			}
		}
	}
	
	public void distributeUniformPrivateData(int n, double epsilon) {
		// 10.000 = 100 * 100;
		new CasperQuery().clearPrivateData();
		double area = (maxx - minx) * (maxy - miny);
		// System.out.println("area = " + area + " nobjects = " + n);
		double sqrtn = Math.sqrt(n);
		double gridx = (maxx - minx) / sqrtn;
		double gridy = (maxy - miny) / sqrtn;
		int isqrtn = (int)sqrtn;
		// System.out.println("sqrtn " + isqrtn);
		
		long cc = 0;
		long oc = 0;
		int nc = 0;
		for (int i = 0; i < isqrtn; i++) {
			for (int j = 0; j < isqrtn; j++) {
				double offsetx = minx + i * gridx;
				double offsety = miny + j * gridy;
				int seedindex =  (i * isqrtn + j);
				Geometry seed = geometriesFromSeed(seedindex);
				Geometry reduce = CasperUtil.reduceGeometry(seed, epsilon);
//				System.out.println("zzz " + reduce.getCoordinates().length);
				if (reduce.getCoordinates().length <= 3) {
//					System.out.println("zzz " + reduce.getCoordinates().length);
					seed = geometriesFromSeed(0);
					reduce = CasperUtil.reduceGeometry(seed, epsilon);
//					System.out.println("zzz " + reduce.getCoordinates().length);
				}
				Point seedcentroid = seed.getCentroid();
				//System.out.println("centroid b " + i + " " + seedcentroid);
				double distx = seedcentroid.getX() - offsetx;
				double disty = seedcentroid.getY() - offsety;
				//System.out.println("dist b " + i + " " + distx + "," + disty);
				for(Coordinate c : seed.getCoordinates()) {
					//System.out.println(c);
					c.x -= distx;
					c.y -= disty;
					//System.out.println(c);
				}
				for(Coordinate c : reduce.getCoordinates()) {
					//System.out.println(c);
					c.x -= distx;
					c.y -= disty;
					//System.out.println(c);
				}
//				System.out.println((i * isqrtn + j) + ", " + seed.getCoordinates().length + ", " + reduce.getCoordinates().length + ", " + seedindex);
				oc += seed.getCoordinates().length;
				new CasperQuery().insertPrivateData(seed, i * isqrtn + j, false);
				//System.out.println(seed.toText());
				//System.out.println(reduce.toText());
				cc += reduce.getCoordinates().length;
				new CasperQuery().insertPrivateData(reduce, i * isqrtn + j, true);
			}
		}
		System.out.println(oc + ", " + cc);
	}
	
	public Geometry geometriesFromSeed(int n) {
			
		String seed = CasperGeometrySeeds.SEEDS[n % CasperGeometrySeeds.SEEDS.length];
		String[] pointsStr = seed.trim().split(" ");
		
		Coordinate[] cs = new Coordinate[pointsStr.length];
		
		for (int i=1;i<pointsStr.length;i++){
			String pointStr = pointsStr[i];
			Matcher matcher = pattern.matcher(pointStr);
			if (matcher.find()){
				double lat = Double.valueOf(matcher.group(2));
				double lng = Double.valueOf(matcher.group(1));
				Coordinate c = new Coordinate(lat, lng);
				cs[i-1] = c;
			}
		}
		cs[cs.length-1] = cs[0];
		Geometry g = new GeometryFactory().createLineString(cs);
		return g;
	}
	
	public Geometry[] geometriesFromSeeds() {
		double scale = 1000000.0f;
		Geometry[] gs = new Geometry[CasperGeometrySeeds.SEEDS.length];
		for (int s = 0; s < CasperGeometrySeeds.SEEDS.length; s++) {
			
			String seed = CasperGeometrySeeds.SEEDS[s];
			//System.out.println(seed);
			String[] pointsStr = seed.trim().split(" ");
			
			Coordinate[] cs = new Coordinate[pointsStr.length];
			
			for (int i=1;i<pointsStr.length;i++){
				String pointStr = pointsStr[i];
				Matcher matcher = pattern.matcher(pointStr);
				if (matcher.find()){
					double lat = Double.valueOf(matcher.group(2));
					double lng = Double.valueOf(matcher.group(1));
					Coordinate c = new Coordinate(lat, lng);
					cs[i-1] = c;
				}
			}
			cs[cs.length-1] = cs[0];
			Geometry g = new GeometryFactory().createLineString(cs);
//			Point gcentroid = g.getCentroid();
//			for (Coordinate c : g.getCoordinates()) {
//				c.x -= gcentroid.getX();
//				c.y -= gcentroid.getY();
//				c.x /= scale;
//				c.y /= scale;
//				c.x += gcentroid.getX();
//				c.y += gcentroid.getY();
//			}
			gs[s] = g;
		}
		return gs;
	}
	
	public Geometry randomGeometryInGeometryNoTranslate(Geometry bound) {
		
		String seed = CasperGeometrySeeds.getRandomSeed();
		
		String[] pointsStr = seed.trim().split(" ");
		
		Coordinate[] cs = new Coordinate[pointsStr.length];
		
		//double translateLat = new Random().nextDouble() * 90 - 45;
		//double translateLng = new Random().nextDouble() * 90 - 45;
		//System.out.println("translate " + translateLat + " . " + translateLng);
		double scaleLat = 10 + new Random().nextDouble() * 2;
		double scaleLng = 10 + new Random().nextDouble() * 2;
		
		for (int i=1;i<pointsStr.length;i++){
			String pointStr = pointsStr[i];
			Matcher matcher = pattern.matcher(pointStr);
			if (matcher.find()){
				double lat = Double.valueOf(matcher.group(2));
				double lng = Double.valueOf(matcher.group(1));
				//System.out.println(String.format("%f, %f", lat, lng));
//				lat *= scaleLat;
//				lng *= scaleLng;
//				lat += translateLat;
//				lng += translateLng;
				//if (lat)
				Coordinate c = new Coordinate(lat, lng);
				cs[i-1] = c;
				//cs[i-1] = new CasperQuery().expand(c, translateLat, translateLng);
			}
		}
		cs[cs.length-1] = cs[0];
		
		/*
		String lineString = "0 0, 0 20, 20 20, 20 0, 0 0";
		try {
			return new WKTReader().read("LINESTRING ("+lineString+")");
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		*/
		Geometry g = new GeometryFactory().createLineString(cs);
		//g = CasperUtil.reduceGeometry(g, 0.0005);
		//System.out.println(g.toText());
		return g;
		//return new GeometryFactory().createLineString(cs);
		//return null;
	}
	
	public Geometry randomGeometryInGeometry(Geometry bound) {
		
		String seed = CasperGeometrySeeds.getRandomSeed();
		
		String[] pointsStr = seed.trim().split(" ");
		
		Coordinate[] cs = new Coordinate[pointsStr.length];
		
		double translateLat = new Random().nextDouble() * 90 - 45;
		double translateLng = new Random().nextDouble() * 90 - 45;
		//System.out.println("translate " + translateLat + " . " + translateLng);
		double scaleLat = 1 + new Random().nextDouble() * 2;
		double scaleLng = 1 + new Random().nextDouble() * 2;
		
		for (int i=1;i<pointsStr.length;i++){
			String pointStr = pointsStr[i];
			Matcher matcher = pattern.matcher(pointStr);
			if (matcher.find()){
				double lat = Double.valueOf(matcher.group(2));
				double lng = Double.valueOf(matcher.group(1));
				//System.out.println(String.format("%f, %f", lat, lng));
//				lat *= scaleLat;
//				lng *= scaleLng;
//				lat += translateLat;
//				lng += translateLng;
				//if (lat)
				Coordinate c = new Coordinate(lat, lng);
				//cs[i-1] = c;
				cs[i-1] = new CasperQuery().expand(c, translateLat, translateLng);
			}
		}
		cs[cs.length-1] = cs[0];
		
		/*
		String lineString = "0 0, 0 20, 20 20, 20 0, 0 0";
		try {
			return new WKTReader().read("LINESTRING ("+lineString+")");
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		*/
		Geometry g = new GeometryFactory().createLineString(cs);
		//g = CasperUtil.reduceGeometry(g, 0.0005);
		//System.out.println(g.toText());
		return g;
		//return new GeometryFactory().createLineString(cs);
		//return null;
	}
	
	public void generateData(int pri, int pub, Geometry bound, double epsilon, boolean original) {
		new CasperQuery().clearAllData();
		System.out.println("Generate Private data");
		for(int i = 0; i < pri; i++) {
			Geometry g = randomGeometryInGeometry(bound);
			//if (original) {
				new CasperQuery().insertPrivateData(g, i, false);
			//} else {
				new CasperQuery().insertPrivateData(CasperUtil.reduceGeometry(g, epsilon), i, true);
			//}
			//if ((i + 1) % 50000 == 0)
				//System.out.println("gen private " + (i + 1));
		}
		System.out.println("Generate Public data");
		for(int i = 0; i < pub; i++) {
			new CasperQuery().insertPublicData(randomCoordinateInGeometry(bound), i);
			if ((i + 1) % 50000 == 0)
				System.out.println("gen public " + (i + 1));
		}
	}
}
